"""
Convert binary log files to CSV format.
This script reads binary log files from the input folder, converts them to CSV files, and saves them in the same folder.
"""
import struct
import csv
import sys
import os
# from tqdm import tqdm

def read_string(f):
    len_bytes = f.read(4)
    if len(len_bytes) < 4:
        return None
    length = struct.unpack('<I', len_bytes)[0]
    return f.read(length).decode('utf-8')

def read_header(f):
    n_bytes = f.read(4)
    if len(n_bytes) < 4:
        raise ValueError("Invalid or empty file.")
    n_fields = struct.unpack('<i', n_bytes)[0]
    # print(f"[DEBUG] n_fields = {n_fields}")
    field_names = []
    for i in range(n_fields):
        name = read_string(f)
        if name is None:
            raise ValueError(f"[ERROR] Field {i} could not be read.")
        field_names.append(name)
    return n_fields, field_names

def read_frame(f, n_fields):
    if n_fields < 2:
        return None

    # 先读取前两个 uint32_t
    ts_bytes = f.read(8)
    if len(ts_bytes) < 8:
        return None
    seconds, nanoseconds = struct.unpack('<II', ts_bytes)

    # 再读取剩下 (n_fields - 2) 个 float
    rest_bytes = f.read(4 * (n_fields - 2))
    if len(rest_bytes) < 4 * (n_fields - 2):
        return None
    values = list(struct.unpack('<' + 'f' * (n_fields - 2), rest_bytes))

    return [seconds, nanoseconds] + values

def convert_logbin_to_csv(bin_path, csv_path):
    file_size = os.path.getsize(bin_path)
    if file_size < 8:
        raise ValueError(f"[ERROR] The file {bin_path} is too small to be a valid log file.")
    
    with open(bin_path, 'rb') as f:
        n_fields, field_names = read_header(f)
        frame_size = 8 + 4 * (n_fields - 2)
        header_size = f.tell()  # 记录读完header后的位置
        n_frames = (file_size - header_size) // frame_size

        with open(csv_path, 'w', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow(field_names)

            for _ in range(n_frames):
                row = read_frame(f, n_fields)
                if row is None:
                    break
                # 判断 seconds_from_epoch 是否为 0（第一列）
                if row[0] == 0:
                    continue
                writer.writerow(row)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage:")
        print("batch conversion: python convert_bin2csv.py <input_folder>")
        print("or single file: python convert_bin2csv.py <input_file>.bin")
        sys.exit(1)

    input = sys.argv[1]
    if os.path.isfile(input) and input.endswith('.bin'):
        # If a single .bin file is provided, convert it directly
        bin_path = input
        csv_path = os.path.splitext(bin_path)[0] + ".csv"
        try:
            if os.path.exists(csv_path):
                print(f"[WARNING] CSV file already exists, skipping conversion: {csv_path}")
            else:
                print(f"[INFO] Converting {os.path.basename(bin_path)} to CSV...")
                convert_logbin_to_csv(bin_path, csv_path)
        except Exception as e:
            print(f"[ERROR] Could not convert {os.path.basename(bin_path)}:")
            print(f"{e}")
        sys.exit(0)

    if not os.path.isdir(input):
        print(f"[ERROR] The specified input folder does not exist: {input}")
        sys.exit(1)

    bin_files = [f for f in os.listdir(input) if f.endswith('.bin')]
    if not bin_files:
        print(f"[ERROR] No .bin files found in the folder: {input}")
        sys.exit(1)

    for bin_file in bin_files:
        bin_path = os.path.join(input, bin_file)
        csv_path = os.path.splitext(bin_path)[0] + ".csv"
        try:
            if os.path.exists(csv_path):
                print(f"[WARNING] CSV file already exists, skipping conversion: {csv_path}")
                continue
        except Exception as e:
            print(f"[ERROR] Could not check existence of {csv_path}: {e}")
            continue
        try:
            print(f"[INFO] Converting {bin_file} to CSV...")
            convert_logbin_to_csv(bin_path, csv_path)
        except Exception as e:
            print(f"[ERROR] Could not convert {bin_file}:")
            print(f"{e}")
            continue
        
    print(f"[INFO] Processed {len(bin_files)} files in total.")
    print("[INFO] Conversions completed.")